using System;
using System.Collections.Generic;
using Com.ContentRendering.Api.RenderEngine.Exceptions;

namespace Com.ContentRendering.Api.RenderEngine.Resolver
{
    /// <summary/>
    internal class ComponentResolver : AbstractResolver
    {
        private List<AbstractResolver> _resolvers;

        /// <summary/>
        internal ComponentResolver(
            InputNodeInterface inputNode,
            InputDataInterface inputData)
        {
            this._resolvers = new List<AbstractResolver>();

            // fetch all of the nodes children
            using (IEnumerator<InputNodeInterface> enumerator = inputNode.FetchChildNodes())
            {
                // read each values of the enumerator
                while (enumerator.MoveNext())
                {
                    InputNodeInterface childInputNode = enumerator.Current;

                    // is it static node ... if so just record it and move on
                    if (childInputNode.IsContent())
                    {
                        // add a leaf resolver
                        this.HandleContent(childInputNode);

                        // move to the next node
                        continue;
                    }

                    // is it an output tag node
                    if (childInputNode.IsOutputTag())
                    {
                        // handle the tag
                        this.HandleOutputTag(childInputNode, inputData);

                        // move to the next node
                        continue;
                    }

                    // is it an output tag node
                    if (childInputNode.IsSubstitutionTag())
                    {
                        // handle the tag
                        this.HandleSubstitutionTag(childInputNode, inputData);

                        // move to the next node
                        continue;
                    }

                    // is it an output tag node
                    if (childInputNode.IsTransparentTag())
                    {
                        // handle the tag
                        this.HandleTransparentTag(childInputNode, inputData);

                        // move to the next node
                        continue;
                    }

                    // is it an output tag node    
                    if (childInputNode.IsIfTag())
                    {
                        // handle the tag
                        this.HandleIfTag(childInputNode, inputData);

                        // move to the next node
                        continue;
                    }

                    // is it an output tag node    
                    if (childInputNode.IsIfNotTag())
                    {
                        // handle the tag
                        this.HandleIfNotTag(childInputNode, inputData);

                        // move to the next node
                        continue;
                    }

                    // something went wrong ... so fail
                    throw new NodeUnknownException();
                }
            }
        }

        /// <summary/>
        private void HandleContent(InputNodeInterface childInputNode)
        {
            this._resolvers.Add(new LeafResolver(childInputNode.Content));
        }

        /// <summary/>
        private void HandleOutputTag(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            using (IEnumerator<InputDataInterface> enumerator = this.FetchData(childInputNode, inputData))
            {
                // read each values of the enumerator
                while (enumerator.MoveNext())
                {
                    this._resolvers.Add(new LeafResolver(enumerator.Current.Value));
                }
            }
        }

        /// <summary/>
        private void HandleSubstitutionTag(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            using (IEnumerator<InputDataInterface> enumerator = this.FetchData(childInputNode, inputData))
            {
                // read each values of the enumerator
                while (enumerator.MoveNext())
                {
                    this._resolvers.Add(new ComponentResolver(
                        childInputNode,
                        enumerator.Current));
                }
            }
        }

        /// <summary/>
        private void HandleTransparentTag(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            using (IEnumerator<InputDataInterface> enumerator = this.FetchData(childInputNode, inputData))
            {
                // read each values of the enumerator
                while (enumerator.MoveNext())
                {
                    // don't add the value from the enumerator ... only add the existing data ... remember
                    // the transparent tag is a conditional tag that presents the parent data node
                    this._resolvers.Add(new ComponentResolver(
                        childInputNode,
                        inputData));
                }
            }
        }

        /// <summary/>
        private void HandleIfTag(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            using (IEnumerator<InputDataInterface> enumerator = this.FetchData(childInputNode, inputData))
            {
                // are there no values
                if (enumerator.MoveNext())
                {
                    // don't add the value from the enumerator ... only add the existing data 
                    this._resolvers.Add(new ComponentResolver(
                        childInputNode,
                        inputData));
                }
            }
        }

        /// <summary/>
        private void HandleIfNotTag(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            using (IEnumerator<InputDataInterface> enumerator = this.FetchData(childInputNode, inputData))
            {
                // are there no values
                if (!enumerator.MoveNext())
                {
                    // don't add the value from the enumerator ... only add the existing data 
                    this._resolvers.Add(new ComponentResolver(
                        childInputNode,
                        inputData));
                }
            }
        }

        /// <summary/>
        private IEnumerator<InputDataInterface> FetchData(
            InputNodeInterface childInputNode,
            InputDataInterface inputData)
        {
            // is the node a parent node?
            if (childInputNode.IsParentTag())
            {
                return inputData.FetchParentData(childInputNode.ParentLevel);
            }

            // is the node a child node?
            if (childInputNode.IsChildTag())
            {
                return inputData.FetchChildData(childInputNode.Name);
            }

            // is the node a child node?
            if (childInputNode.IsRootTag())
            {
                return inputData.FetchRootData(childInputNode.Name);
            }

            // the node is the root node
            return null;
        }

        /// <summary/>
        internal override bool IsParent()
        {
            return true;
        }

        /// <summary/>
        internal override IEnumerator<AbstractResolver> Children
        {
            get { return this._resolvers.GetEnumerator(); }
        }

        /// <summary/>
        internal override string Value
        {
            get { throw new ResolverIsParentException(); }
        }
    }
}
